#include "armaExtension.h"


/****************************************************************************************
*
*	MathLink GetValue Extension
*
****************************************************************************************/

bool MLGetValue(MLINK mlp, arma::uvec* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	int size = args;
	if(value->mem_state == 3) // fixed size.
	{
		size = value->size();
		value->fill(arma::fill::zeros); // The may be too few args.
	}
	else
		value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		size_t tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result && a < size)
			(*value)(a) = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, arma::ivec* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	int size = args;
	if(value->mem_state == 3) // fixed size.
	{
		size = value->size();
		value->fill(arma::fill::zeros); // The may be too few args.
	}
	else
		value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		long tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result && a < size)
			(*value)(a) = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, arma::vec* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	int size = args;
	if(value->mem_state == 3) // fixed size.
	{
		size = value->size();
		value->fill(arma::fill::zeros); // The may be too few args.
	}
	else
		value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		double tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result && a < size)
			(*value)(a) = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, arma::cx_vec* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	int size = args;
	if(value->mem_state == 3) // fixed size.
	{
		size = value->size();
		value->fill(arma::fill::zeros); // The may be too few args.
	}
	else
		value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		complex tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result && a < size)
			(*value)(a) = tmp;
	}

	return result;
}


bool MLGetValue(MLINK mlp, std::vector<arma::uvec>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::uvec tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::ivec>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::ivec tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::vec>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::vec tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::cx_vec>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::cx_vec tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::uvec4>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::uvec tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::ivec4>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::ivec4 tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::vec4>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::vec4 tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

bool MLGetValue(MLINK mlp, std::vector<arma::cx_vec4>* value){
	int args;
	bool result = (MLTestHead(mlp,"List",&args));
	value->resize(args);

	for(int a = 0; a < args && result; a++)
	{
		arma::cx_vec4 tmp;
		result &= MLGetValue(mlp,&tmp);
		if(result)
			(*value)[a] = tmp;
	}

	return result;
}

/****************************************************************************************
*
*	MathLink PutValue Extension
*
****************************************************************************************/

bool MLPutValue(MLINK mlp, const arma::sp_cx_mat &value){
	bool result = MLPutFunction(mlp,"SparseArray",2);

	if(!result)
		return false;

	size_t elements = value.n_nonzero;

	result = MLPutFunction(mlp,"List",elements);

	size_t currentcol = 0;

	for(size_t e = 0; e < elements && result; e++)
	{
		result &= MLPutFunction(mlp,"Rule",2); if(!result) continue;
		result &= MLPutFunction(mlp,"List",2); if(!result) continue;
		result &= MLPutValue(mlp,value.row_indices[e]+1); if(!result) continue;
		while(currentcol < value.n_cols && value.col_ptrs[currentcol+1] <= e) 
			currentcol++;
		result &= MLPutValue(mlp,currentcol+1); if(!result) continue;
		result &= MLPutValue(mlp,value.values[e]);
	}
	
	if(!result)	return false;
	
	result = MLPutFunction(mlp,"List",2); if(!result) return false;
	result &= MLPutValue(mlp,value.n_rows); if(!result) return false;
	result &= MLPutValue(mlp,value.n_cols); if(!result) return false;

	return result;
}

bool MLPutValue(MLINK mlp, arma::vec2 value){
	bool result = MLPutFunction(mlp,"List",2);

	if(!result)
		return false;
	
	result &= MLPutValue(mlp,value(0));
	if(!result) 
		return false;
	result &= MLPutValue(mlp,value(1));

	return result;
}

bool MLPutValue(MLINK mlp, std::pair<arma::vec2,double> value){
	bool result = MLPutFunction(mlp,"List",2);

	if(!result)
		return false;
	
	result &= MLPutValue(mlp,value.first);
	if(!result) 
		return false;
	result &= MLPutValue(mlp,value.second);

	return result;
}

bool MLPutValue(MLINK mlp, arma::field<std::pair<arma::vec2,double>> value){
	bool result = MLPutFunction(mlp,"List",value.n_cols);
	if(!result)
		return false;
	
	for(size_t c = 0; c < value.n_cols && result; c++)
	{
		result = MLPutFunction(mlp,"List",value.n_rows);
		for(size_t r = 0; r < value.n_rows && result; r++)
		{
			result &= MLPutValue(mlp,value(r,c));
		}
	}
	
	return result;
}
